home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
C
/
Snippets
/
Stuart's Tech Notes
/
SmartZoomWindow.c
< prev
next >
Wrap
Text File
|
1995-06-08
|
7KB
|
159 lines
/* (C) June 1994 Stuart Cheshire <cheshire@cs.stanford.edu>
In a stunning piece of developer support, when Apple added the zoom box
to the Window Manager, the user interface police mandated how it should
work in a user-friendly way, and then provided a system call that didn't
do it! Considering that at the time many developers (especially shareware
authors) didn'it have access to large screens or multiple-monitor setups,
expecting every developer to engineer their own user-friendly multiple-
monitor-aware solution was ridiculous.
Having just been through this grief for Bolo, I'm making the solution
available for other developers.
SmartZoomWindow is written to be a simple drop-in replacement for Apple's
ZoomWindow call so you shouldn't have to make much change to your program.
The first three parameters are the same, and the fourth is a pointer to a
Rectangle giving the maximum and minimum sensible dimensions that the window
should be zoomed to.
ZoomWindow(WindowPtr theWindow, short partCode, Boolean front);
SmartZoomWindow(WindowPtr win, short partCode, Boolean front, const Rect *Limits);
The Rect is defined in a way compatible to GrowWindow -- ie top,left give the
minimum height and width of the window, and bottom, right give the maximum
height and width of the window *plus one*. Note that if you want the maximum
window size to be 100x100, this means that you have to set the limits to 101.
I know that this is bizarre, but it's the way that GrowWindow works, even
though that's not what Inside Macintosh says. (As an aside, I wonder how
many programs are bitten by this? I know the Finder has this bug -- open the
"About This Macintosh" window and try resizing it with the zoom box and with
the grow box. If you look closely you will see that when you repeatedly click
on the zoom box, the width oscillates in and out by one pixel.)
If you don't have any special constraints on what size the window should
be, you can set the minimums to zero and the maximums to 0x7FFF, and
SmartZoomWindow will just take care of zooming the window up to the full
size of the monitor that it is on, instead of zooming it back to the main
(menu bar) screen like the normal ZoomWindow call does.
After calling WaitNextEvent and FindWindow as usual, your code should proceed
like this:
if (TrackBox(win, theEventRecord->where, partcode))
{
Rect Limits = { 0, 0, 0x7FFF, 0x7FFF }; // Or whatever limits are appropriate
SetPort(win);
EraseRect(&win->portRect);
SmartZoomWindow(win, partcode, TRUE, &Limits);
}
SmartZoomWindow is slightly smarter than the algorithm that the Finder uses,
which makes the windows fly up to the top left corner of the monitor if
resizing them in the current position would result in part of the window going
off the screen. SmartZoomWindow will just move the window the minimum amount
required to keep it on the screen.
Some of the inspiration for this code came from DoBetterWZoom.c on the Apple
Developer CD. I would credit the author, but there is no name on that code.
*/
#include <StuTypes.h>
local GDHandle findDominantDevice(Rect *theRect)
{
long sectArea, greatestArea = 0;
GDHandle dominantGDevice = NULL, dev = GetDeviceList();
while (dev) // for every device
{
if (TestDeviceAttribute(dev, screenDevice) && // that's an active screen
TestDeviceAttribute(dev, screenActive))
{
Rect r;
SectRect(theRect, &(**dev).gdRect, &r); // find out the intersection area
sectArea = (long)(r.right - r.left) * (long)(r.bottom - r.top);
if (greatestArea < sectArea) { greatestArea = sectArea; dominantGDevice = dev; }
}
dev = GetNextDevice(dev);
}
return(dominantGDevice);
}
// The window will be zoomed to fill the screen it is already on,
// subject to the size constraints of the Limits rectangle.
// win = window to be zoomed
// partCode = inZoomIn or inZoomOut
// front = Bring window to front after zooming? (Normally TRUE)
// Limits.top = minimum allowable window height
// Limits.left = minimum allowable window width
// Limits.bottom = maximum sensible window height + 1
// Limits.right = maximum sensible window width + 1
// Note: The maximums are actually one greater than the actual maximum allowed sizes,
// for consistency with GrowWindow
local void SmartZoomWindow(WindowPtr win, short partCode, Boolean front, const Rect *Limits)
{
// Zooming in is easy. Only zooming out needs extra smarts
if (partCode == inZoomOut)
{
SysEnvRec sysenvirons;
short hsize = Limits->right - 1;
short vsize = Limits->bottom - 1;
WindowPeek wp = (WindowPeek)win;
Rect *zoomRect = &(*(WStateDataHandle)wp->dataHandle)->stdState;
Rect *globalFrameRect = &(*wp->strucRgn)->rgnBBox;
Rect globalPortRect = win->portRect;
Rect target = qd.screenBits.bounds; // Start with a reasonable default
target.top += GetMBarHeight();
LocalToGlobal(&topLeft(globalPortRect)); // calculate the window's portRect
LocalToGlobal(&botRight(globalPortRect)); // in global coordinates
// *** TASK 1
// Pick a monitor (if necessary) and work out the maximum allowable rectangle on it
SysEnvirons(1,&sysenvirons);
if (sysenvirons.hasColorQD)
{
GDHandle dev = findDominantDevice(globalFrameRect);
if (dev)
{
target = (*dev)->gdRect;
if (dev == GetMainDevice()) target.top += GetMBarHeight();
}
}
// *** TASK 2
// Reduce the logical target rectangle a little to allow for the frame
// around the window, and a couple of pixels extra for aesthetic reasons
target.top += (globalPortRect.top - globalFrameRect->top ) + 2;
target.left += (globalPortRect.left - globalFrameRect->left ) + 2;
target.bottom += (globalPortRect.bottom - globalFrameRect->bottom) - 1;
target.right += (globalPortRect.right - globalFrameRect->right ) - 1;
// *** TASK 3
// Make sure the target rectangle is big enough for the minimum size
if (target.bottom < target.top + Limits->top ) target.bottom = target.top + Limits->top;
if (target.right < target.left + Limits->left) target.right = target.left + Limits->left;
// *** TASK 4
// Make sure the desired (maximum) size fits within the target rectangle
if (vsize > target.bottom - target.top) vsize = target.bottom - target.top;
if (hsize > target.right - target.left) hsize = target.right - target.left;
// *** TASK 5
// Start off with a zoom rectangle obtained by simply resizing the window
// in the current position
zoomRect->top = globalPortRect.top;
zoomRect->left = globalPortRect.left;
zoomRect->bottom = globalPortRect.top + vsize;
zoomRect->right = globalPortRect.left + hsize;
// *** TASK 6
// If Window is falls outside the limits, shift it the minimum amount to bring it inside
if (zoomRect->top < target.top ) OffsetRect(zoomRect, 0, target.top - zoomRect->top);
if (zoomRect->left < target.left ) OffsetRect(zoomRect, target.left - zoomRect->left, 0);
if (zoomRect->bottom > target.bottom) OffsetRect(zoomRect, 0, target.bottom - zoomRect->bottom);
if (zoomRect->right > target.right ) OffsetRect(zoomRect, target.right - zoomRect->right, 0);
}
ZoomWindow(win, partCode, front);
}